package com.bigknow.appstore.web.base.service.impl;

import com.bigknow.appstore.proxy.entity.ProxyMeta;
import com.bigknow.appstore.proxy.entity.SecurityMeta;
import com.bigknow.appstore.proxy.enumerate.LogLevel;
import com.bigknow.appstore.proxy.enumerate.SecurityType;
import com.bigknow.appstore.web.base.dao.IDictDao;
import com.bigknow.appstore.web.base.dao.IPermissionDao;
import com.bigknow.appstore.web.base.dao.IRoleDao;
import com.bigknow.appstore.web.base.entity.Dict;
import com.bigknow.appstore.web.base.entity.Permission;
import com.bigknow.appstore.web.base.entity.Role;
import com.bigknow.appstore.web.base.entity.User;
import com.bigknow.appstore.web.base.service.IConsoleService;
import com.bigknow.appstore.web.base.service.IUserService;
import com.bigknow.appstore.web.base.service.ServiceUtil;
import com.bigknow.appstore.web.framework.common.BaseLogger;
import com.bigknow.appstore.web.base.entity.enums.State;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.mongodb.morphia.Datastore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by TwelveFoldEr on 2016/3/11.
 */
@Service
public class ConsoleService extends BaseLogger implements IConsoleService {
    private String dataDir = "config/data";
    @Autowired
    private IDictDao dictDao;
    @Autowired
    private IPermissionDao permissionDao;
    @Autowired
    private IRoleDao roleDao;
    @Autowired
    private IUserService userService;
    private SAXReader reader = new SAXReader();

    @Autowired
    private Datastore ds;

    @Override
    public void initConsole() {
        initDict();
        initMenu();
        initRole();
        initUser();
        initProxyMeta();
    }


    @Override
    public void updateConsole() {
        updateMenu();
        updateRole();
        updateUser();
        initDict();
        initProxyMeta();
    }

    /**
     * 由于目前去掉了权限配置模块，所以设置此方法用于更新目录
     */
    @Override
    public void updateMenu() {
        initMenu();
    }

    @Override
    public void updateUser() {
        initUser();
    }

    @Override
    public void updateRole() {
        //如果不存在就创建
        if (ds.find(Role.class).field("code").equal("clusterManager").get() == null
                || ds.find(Role.class).field("code").equal("consumer").get() == null
                || ds.find(Role.class).field("code").equal("nodeManager").get() == null) {
            initRole();
        }
    }


    private void initProxyMeta() {
        if (ds.find(ProxyMeta.class).field("id").equal("1").get() == null) {
            ProxyMeta pm = new ProxyMeta();
            pm.setId("1");
            pm.setHTTPS(false);
            pm.setKeepAlive(true);
            pm.setLastRequestTimeout(30000);
            pm.setLogLevel(LogLevel.INFO);
            pm.setName("http");
            pm.setPort(9000);
            pm.setSyncModel(false);
            pm.setRunAuto(true);
            pm.setTimeout(30000);
            pm.setBacklog(200);
            pm.setUseLastRequest(true);

            SecurityMeta sec = new SecurityMeta();
            sec.setSecurityType(SecurityType.CALL_ERRORS_FILTER_SEC);
            sec.getPolicyAttrs().put("errorTimes", "5");
            sec.getPolicyAttrs().put("disConnectTimeout", "10000");
            pm.getSecMaps().put(SecurityType.CALL_ERRORS_FILTER_SEC, sec);
            ds.save(pm);
        }
        if (ds.find(ProxyMeta.class).field("id").equal("2").get() == null) {
            ProxyMeta pm = new ProxyMeta();
            pm.setId("2");
            pm.setHTTPS(true);
            pm.setKeepAlive(true);
            pm.setLastRequestTimeout(30000);
            pm.setLogLevel(LogLevel.INFO);
            pm.setName("https");
            pm.setPort(9100);
            pm.setSyncModel(false);
            pm.setRunAuto(true);
            pm.setTimeout(30000);
            pm.setBacklog(200);
            pm.setUseLastRequest(true);

            SecurityMeta sec = new SecurityMeta();
            sec.setSecurityType(SecurityType.CALL_ERRORS_FILTER_SEC);
            sec.getPolicyAttrs().put("errorTimes", "5");
            sec.getPolicyAttrs().put("disConnectTimeout", "10000");
            pm.getSecMaps().put(SecurityType.CALL_ERRORS_FILTER_SEC, sec);
            ds.save(pm);
        }
    }

    private void findAndSave(User user, String roleCode) {
        if (ds.find(User.class).field("account").equal(user.getAccount()).get() != null) return;
        Role role = new Role();
        role.setCode(roleCode);
//        String ids = ServiceUtil.combineIds(ds.find(Role.class).field("code").equal(roleCode).asList());
        user.setRoleIds("1");
        this.userService.save(user);
    }

    private void initUser() {
        User user = new User();
        user.setId("1");
        user.setAccount("admin");
        user.setPassword("admin");
        user.setName("系统管理员");
        user.setState(State.ACTIVE);

        findAndSave(user, "admin");
    }

    @Override
    public boolean hasInit() {
        if (this.userService.getCount() == 0 || this.permissionDao.getCount() == 0) {
            return false;
        }

        return true;
    }

    private void initDict() {
        try {
            List<Element> rows = getElements("default-dict-data.xml");
            rows.forEach(row -> {
                List<Element> fields = row.elements("field");
                Dict dict = new Dict();

                fields.forEach(field -> {
                    switch (field.attribute("name").getValue().toString()) {
                        case "id":
                            dict.setId(field.getTextTrim());
                            break;
                        case "code":
                            dict.setCode(field.getTextTrim());
                            break;
                        case "name":
                            dict.setName(field.getTextTrim());
                            break;
                        case "group":
                            dict.setGroup(field.getTextTrim());
                            break;
                        case "sort":
                            dict.setSort(new Integer(field.getTextTrim()));
                            break;
                        case "remark":
                            dict.setRemark(field.getTextTrim());
                            break;
                        case "state":
                            dict.setState(findState(field));
                            break;
                    }

                    displayRecord(field);
                });
                this.dictDao.save(dict);
            });

        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    private void initMenu() {
        try {
            List<Element> rows = getElements("default-menu-data.xml");
            rows.forEach(row -> {
                List<Element> fields = row.elements("field");
                Permission p = new Permission();
                fields.forEach(field -> {
                    switch (field.attribute("name").getValue().toString()) {
                        case "id":
                            p.setId(field.getTextTrim());
                            break;
                        case "code":
                            p.setCode(field.getTextTrim());
                            break;
                        case "name":
                            p.setName(field.getTextTrim());
                            break;
                        case "parentCode":
                            p.setParentCode(field.getTextTrim());
                            break;
                        case "pageUrl":
                            p.setPageUrl(field.getTextTrim());
                            break;
                        case "type":
                            switch (field.getTextTrim()) {
                                case "MENU":
                                    p.setType(Permission.Type.MENU);
                                    break;
                                case "ACTION":
                                    p.setType(Permission.Type.ACTION);
                                    break;
                                case "DATA":
                                    p.setType(Permission.Type.DATA);
                                    break;
                            }
                            break;
                        case "state":
                            p.setState(findState(field));
                            break;
                        case "sort":
                            p.setSort(new Integer(field.getTextTrim()));
                            break;
                        case "iconCss":
                            p.setIconCss(field.getTextTrim());
                            break;
                    }

                    displayRecord(field);
                });
                this.permissionDao.save(p);
            });

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initRole() {
        try {
            List<Element> rows = getElements("default-role-data.xml");
            Map<String, Role> roleMap = new HashMap<>();
            rows.forEach(row -> {
                List<Element> fields = row.elements("field");
                Role role = new Role();
                fields.forEach(field -> {
                    switch (field.attribute("name").getValue().toString()) {
                        case "id":
                            role.setId(field.getTextTrim());
                            break;
                        case "code":
                            role.setCode(field.getTextTrim());
                            break;
                        case "name":
                            role.setName(field.getTextTrim());
                            break;
                        case "state":
                            role.setState(findState(field));
                            break;
                        case "permissionIds":
                            role.setPermissionIds(field.getText().trim());
                    }

                    displayRecord(field);
                });

/*                if ("admin".equalsIgnoreCase(role.getCode())) {
                    String ids = ServiceUtil.combineIds(this.permissionDao.findAll());
                    role.setPermissionIds(ids);
                }*/

                roleMap.put(role.getId(), role);
            });
            configSymbolPermissions(roleMap);

            roleMap.values().forEach(role -> roleDao.save(role));

        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    private void configSymbolPermissions(Map<String, Role> roleMap) {
        roleMap.values().forEach(role -> {
                    String[] perms = role.getPermissionIds().split(",");
                    for (String perm : perms) {
                        if (isSymbol(perm)) {
                            String addPerms = roleMap.get(getRoleIdBySymbol(perm)).getPermissionIds();
                            role.setPermissionIds(role.getPermissionIds().replace(perm, addPerms));
                        }
                    }
                }
        );
    }

    private boolean isSymbol(String perm) {
        return perm.startsWith("${");
    }

    private String getRoleIdBySymbol(String perm) {
        return perm.substring(2, perm.length() - 1);
    }

    private File getDataFile(String filePath) {
        return new File(this.getClass().getClassLoader()
                .getResource(dataDir + "/" + filePath).getFile());
    }

    private List<Element> getElements(String fileName) throws DocumentException {
        Document document = reader.read(getDataFile(fileName));

        Element root = document.getRootElement();
        return root.elements("row");
    }

    private void displayRecord(Element field) {
        this.logger.info("Field " + field.attribute("name").getValue() + " value " + field.getTextTrim());
    }

    private State findState(Element field) {
        switch (field.getTextTrim()) {
            case "ACTIVE":
                return State.ACTIVE;
            case "DISABLED":
                return State.DISABLED;
            default:
                return State.DISABLED;
        }
    }
}